/**
 * 
 */
package org.softTank.BpelTranslator;

import java.util.List;
import java.util.Stack;

import org.softTank.BpelModel.ObjectFactory;
import org.softTank.BpelModel.TAssign;
import org.softTank.BpelModel.TCondition;
import org.softTank.BpelModel.TCopy;
import org.softTank.BpelModel.TFrom;
import org.softTank.BpelModel.TLink;
import org.softTank.BpelModel.TLinks;
import org.softTank.BpelModel.TSource;
import org.softTank.BpelModel.TTarget;
import org.softTank.BpelModel.TTargets;
import org.softTank.BpelModel.TTo;

import cn.softTank.BcpModel.BcpActivity;
import cn.softTank.BcpModel.BcpAssign;
import cn.softTank.BcpModel.BcpControl;
import cn.softTank.BcpModel.BcpControlType;
import cn.softTank.translator.Counter;

/**
 * @author Yuzone
 * 
 */
public class RelationContainer {

	protected BcpControl control;
	protected TAssign assign;
	protected ObjectFactory of;
	protected int count;
	protected Stack<TSource> sourceStack;
	protected Stack<TTargets> targetsStack;

	public RelationContainer() {
		sourceStack = new Stack<TSource>();
		targetsStack = new Stack<TTargets>();
		of = new ObjectFactory();
	}

	public RelationContainer(BcpControl control, TLinks links, Counter value) {
		this();
		this.control = control;
		parser(links, value);
	}

	public List<BcpActivity> getSuperiors() {
		if (control != null)
			return control.getSuperiors();
		return null;
	}

	public List<BcpActivity> getInferiors() {
		if (control != null)
			return control.getInferiors();
		return null;
	}

	public TAssign getAssign() {
		return assign;
	}

	public Stack<TSource> getSourceStack() {
		return sourceStack;
	}

	public Stack<TTargets> getTargetsStack() {
		return targetsStack;
	}

	public void setControl(BcpControl control) {
		this.control = control;
	}

	private void parser(TLinks links, Counter value) {
		if (control != null) {
			this.assign = createAssign(links, value);

			switch (getType()) {
			case BcpControlType.AND_JOIN:
				createAJ(links, value);
				break;
			case BcpControlType.OR_JOIN:
				createOJ(links, value);
				break;
			case BcpControlType.XOR_JOIN:
				break;
			case BcpControlType.AND_SPLIT:
				createAS(links, value);
				break;
			case BcpControlType.OR_SPLIT:
				break;
			case BcpControlType.XOR_SPLIT:
				break;
			case BcpControlType.SEQUENCE:
				createSequ(links, value);
				break;
			default:
			}
		}
	}

	/**
	 * Create "link"s for "AndJoin" relation. Create "link"s between superiors
	 * and "assign", and push "source"s in stack; Create "link"s between
	 * "assign" and inferior, and push "targets" in stack.
	 */
	private void createAJ(TLinks links, Counter value) {
		TTargets targets = of.createTTargets();
		StringBuffer expr = new StringBuffer();

		// Create targets for assign and source for superiors.
		for (int i = 0; i < getCount(); i++) {
			// Create link.
			TLink link = of.createTLink();
			link.setName("link" + value.getCount());
			links.getLink().add(link);

			// Create source.
			TSource source = of.createTSource();
			source.setLinkName(link.getName());
			sourceStack.push(source);

			if (i != 0) {
				expr.append("and");
			}
			expr.append("$link" + value.getCount());

			// Create targets.
			TTarget target = of.createTTarget();
			target.setLinkName(link.getName());
			targets.getTarget().add(target);

			value.increase();
		}

		// Add targets to assign.
		TCondition condition = of.createTCondition();
		condition.getContent().add(expr.toString());
		targets.setJoinCondition(condition);
		assign.setTargets(targets);

		TLink link = of.createTLink();
		link.setName("link" + value.getCount());
		links.getLink().add(link);

		// Add source to assign.
		TSource source = of.createTSource();
		source.setLinkName(link.getName());
		assign.getSources().getSource().add(source);

		// Create targets for inferiors.
		targets = of.createTTargets();
		TTarget target = of.createTTarget();
		target.setLinkName(link.getName());
		targets.getTarget().add(target);
		targetsStack.push(targets);

		value.increase();
	}

	/**
	 * Create "link"s for "OrJoin" relation. Create "link"s between superiors
	 * and "assign", and push "source"s in stack; Create "link"s between
	 * "assign" and inferior, and push "targets" in stack.
	 */
	private void createOJ(TLinks links, Counter value) {
		TTargets targets = of.createTTargets();
		StringBuffer expr = new StringBuffer();

		// Create targets for assign and source for superiors.
		for (int i = 0; i < getCount(); i++) {
			// Create link.
			TLink link = of.createTLink();
			link.setName("link" + value.getCount());
			links.getLink().add(link);

			// Create source.
			TSource source = of.createTSource();
			source.setLinkName(link.getName());
			sourceStack.push(source);

			if (i != 0) {
				expr.append("or");
			}
			expr.append("$link" + value.getCount());

			// Create targets.
			TTarget target = of.createTTarget();
			target.setLinkName(link.getName());
			targets.getTarget().add(target);

			value.increase();
		}

		// Add targets to assign.
		TCondition condition = of.createTCondition();
		condition.getContent().add(expr.toString());
		targets.setJoinCondition(condition);
		assign.setTargets(targets);

		TLink link = of.createTLink();
		link.setName("link" + value.getCount());
		links.getLink().add(link);

		// Add source to assign.
		TSource source = of.createTSource();
		source.setLinkName(link.getName());
		assign.getSources().getSource().add(source);

		// Create targets for inferiors.
		targets = of.createTTargets();
		TTarget target = of.createTTarget();
		target.setLinkName(link.getName());
		targets.getTarget().add(target);
		targetsStack.push(targets);

		value.increase();
	}

	/**
	 * Create "link"s for "AndSplit" relation. Create "link"s between superior
	 * and "assign", and push "source"s in stack; Create "link"s between
	 * "assign" and inferiors, and push "targets"s in stack.
	 */
	private void createAS(TLinks links, Counter value) {
		// Add sources to assign and create targets for inferiors.
		for (int i = 0; i < getCount(); i++) {
			// Create link.
			TLink link = of.createTLink();
			link.setName("link" + value.getCount());
			links.getLink().add(link);

			// Create source.
			TSource s = of.createTSource();
			s.setLinkName(link.getName());
			assign.getSources().getSource().add(s);

			// Create target.
			TTargets ts = of.createTTargets();
			TTarget t = of.createTTarget();
			t.setLinkName(link.getName());
			ts.getTarget().add(t);
			targetsStack.push(ts);

			// counter + 1.
			value.increase();
		}

		// Create source for superiors.
		TLink link = of.createTLink();
		link.setName("link" + value.getCount());
		links.getLink().add(link);
		TSource ts = of.createTSource();
		ts.setLinkName(link.getName());
		sourceStack.push(ts);

		// Add targets to assign.
		TTargets tts = of.createTTargets();
		TTarget t = of.createTTarget();
		t.setLinkName(link.getName());
		tts.getTarget().add(t);
		assign.setTargets(tts);

		value.increase();
	}

	/**
	 * Create "link"s for "Sequence" relation. Create one pair of "link" between
	 * inferior and "assign", and push "targets" in stack. Create another pair
	 * of "link" between superior and "assign", and push "source" in stack.
	 */
	private void createSequ(TLinks links, Counter value) {
		// Create a link.
		TLink link = of.createTLink();
		link.setName("link" + value.getCount());
		value.increase();
		links.getLink().add(link);

		// Create source for superior.
		TSource source = of.createTSource();
		source.setLinkName(link.getName());
		sourceStack.push(source);

		// Add targets to assign.
		TTargets targets = of.createTTargets();
		TTarget target = of.createTTarget();
		target.setLinkName(link.getName());
		targets.getTarget().add(target);
		assign.setTargets(targets);

		// Create a new Link.
		link = of.createTLink();
		link.setName("link" + value.getCount());
		value.increase();
		links.getLink().add(link);

		// Add source to assign.
		source = of.createTSource();
		source.setLinkName(link.getName());
		assign.getSources().getSource().add(source);

		// Create targets for inferior.
		targets = of.createTTargets();
		target = of.createTTarget();
		target.setLinkName(link.getName());
		targets.getTarget().add(target);
		targetsStack.push(targets);
	}

	public int getCount() {
		if (control != null) {
			switch (getType()) {
			case BcpControlType.AND_JOIN:
			case BcpControlType.OR_JOIN:
			case BcpControlType.XOR_JOIN:
				count = control.getSuperiors().size();
				break;
			case BcpControlType.AND_SPLIT:
			case BcpControlType.OR_SPLIT:
			case BcpControlType.XOR_SPLIT:
				count = control.getInferiors().size();
				break;
			default:
			}
		}

		return count;
	}

	private TAssign createAssign(TLinks links, Counter value) {
		TAssign tAssign = of.createTAssign();
		if (control.getAssignList() != null) {
			List<BcpAssign> copyList = control.getAssignList();
			for (BcpAssign ba : copyList) {
				TCopy copy = of.createTCopy();
				TFrom from = of.createTFrom();
				// if (ba.getFrom().startsWith("$")) {
				from.getContent().add(ba.getFrom());
				// } else {
				// from.setLiteral("<literal>" + ba.getFrom() + "</literal>");
				// }
				copy.setFrom(from);
				TTo to = of.createTTo();
				// if (ba.getTowards().startsWith("$")) {
				to.getContent().add(ba.getTowards());
				// } else {
				// to.setLiteral("<literal>" + ba.getTowards() + "</literal>");
				// }
				copy.setTo(to);
				tAssign.getCopyOrExtensionAssignOperation().add(copy);
			}
		}
		tAssign.setSources(of.createTSources());

		return tAssign;
	}

	public int getType() {
		if (control != null)
			return control.getType();
		return -1;
	}
}
